from argo.jdom import JsonNodeBuilders
from argo.format import PrettyJsonFormatter, CompactJsonFormatter
from java.util import Date
from java.io import StringWriter
from java.text import SimpleDateFormat
import functools

class PysonProperty(object):
    name = 0
    defaultValueFn = None

    def __init__(self, defVal=None):
        if defVal is None:
            self.defaultValueFn = None
        elif callable(defVal):
            self.defaultValueFn = defVal
        else:
            self.defaultValueFn = lambda: defVal

    def internalName(self):
        return "__%s" % self.name

    def __get__(self, obj, type=None):
        if hasattr(obj, self.internalName()):
            return getattr(obj, self.internalName())
        else:
            if self.defaultValueFn is not None:
                newValue = self.defaultValueFn()
                self.__set__(obj, newValue)
                return newValue
            else:
                return None

    def __set__(self, obj, value):
        setattr(obj, self.internalName(), value)

    def __delete__(self, obj): pass

    def isdirty(self, obj):
        return hasattr(obj, self.internalName()) or self.defaultValueFn is not None

class PysonPropertyBuilder(PysonProperty):
    def build(self, obj, objectBuilder):
        if self.isdirty(obj):
            self.buildProperty(obj, objectBuilder)

    def buildProperty(self, obj, objectBuilder): pass

class PysonObjectMeta(type):
    def __init__(self, name, bases, members):
        super(PysonObjectMeta, self).__init__(name, bases, members)
        setattr(self, "_%s__super" % name, super(self))

        props = {}
        for base in bases:
            if hasattr(base, "properties"):
                props.update(getattr(base, "properties"))

        for k, v in members.iteritems():
            if (isinstance(v, PysonPropertyBuilder)):
                v.name = k
                props[k] = v

        setattr(self, "properties", props)

class PysonObject(object):
    __metaclass__ = PysonObjectMeta

    def createBuilder(self):
        objBuilder = JsonNodeBuilders.anObjectBuilder()
        for v in self.properties.values():
            v.build(self, objBuilder)
        return objBuilder

def format(builder, formatter):
    writer = StringWriter()
    jsonNode = builder.build()
    formatter.format(jsonNode, writer)
    return writer.toString()

def prettyFormat(builder):
    return format(builder, PrettyJsonFormatter())

def compactFormat(builder):
    return format(builder, CompactJsonFormatter())

"""

The following section contains declarations of most common property types.

"""

stringBuilder = JsonNodeBuilders.aStringBuilder

class string(PysonPropertyBuilder):
    @staticmethod
    def builder(value):
        return stringBuilder(value)

    def buildProperty(self, obj, objectBuilder):
        objectBuilder.withField(self.name, string.builder(self.__get__(obj)))

class boolean(PysonPropertyBuilder):
    @staticmethod
    def builder(value):
        if value:
            return JsonNodeBuilders.aTrueBuilder()
        else:
            return JsonNodeBuilders.aFalseBuilder()

    def buildProperty(self, obj, objectBuilder):
        objectBuilder.withField(self.name, boolean.builder(self.__get__(obj)))

class number(PysonPropertyBuilder):
    @staticmethod
    def builder(value):
        return JsonNodeBuilders.aNumberBuilder(`value`)

    def buildProperty(self, obj, objectBuilder):
        objectBuilder.withField(self.name, number.builder(self.__get__(obj)))

dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")

class date(PysonPropertyBuilder):
    @staticmethod
    def builder(value):
        return string.builder(dateFormat.format(value))

    def buildProperty(self, obj, objectBuilder):
        objectBuilder.withField(self.name, date.builder(self.__get__(obj)))

class array(PysonPropertyBuilder):
    def __init__(self, ebuilder, defVal=None):
        super(array, self).__init__(defVal)
        if ebuilder.__class__ == type and issubclass(ebuilder, PysonProperty):
            self.__elementBuilder = ebuilder.builder
        else:
            self.__elementBuilder = ebuilder

    @staticmethod
    def builder(ebuilder, value):
        arrayBuilder = JsonNodeBuilders.anArrayBuilder()
        for el in value:
            arrayBuilder.withElement(ebuilder(el))
        return arrayBuilder

    @staticmethod
    def elem(ebuilder):
        if ebuilder.__class__ == type and issubclass(ebuilder, PysonProperty):
            return functools.partial(array.builder, ebuilder.builder)
        else:
            return functools.partial(array.builder, ebuilder)

    def buildProperty(self, obj, objectBuilder):
        value = self.__get__(obj)
        objectBuilder.withField(self.name, array.builder(self.__elementBuilder, value))


class struct(PysonPropertyBuilder):
    @staticmethod
    def builder(value):
        assert isinstance(value, PysonObject)
        return value.createBuilder()

    def buildProperty(self, obj, objectBuilder):
        objectBuilder.withField(self.name, struct.builder(self.__get__(obj)))